package ua.yandex.skipass;

import org.joda.time.DateTime;
import org.joda.time.DateTime.Property;
import org.joda.time.DateTimeConstants;

import java.util.Date;

public class SkiPassSystem {
    public static final int MORNING_HOUR_START = 9;
    public static final int MORNING_HOUR_END = 13;
    public static final int EVENING_HOUR_START = 13;
    public static final int EVENING_HOUR_END = 17;

    private int skiPassIdCounter = 1;

    public SkiPass issueWorkdaysSkiPassForMorning(DateTime startingDay) {
        checkWorkday(startingDay);

        return issueSkiPassForPartOfDay(startingDay,
                MORNING_HOUR_START, MORNING_HOUR_END);
    }

    public SkiPass issueWorkdaysSkiPassForEvening(DateTime startingDay) {
        checkWorkday(startingDay);

        return issueSkiPassForPartOfDay(startingDay,
                MORNING_HOUR_START, MORNING_HOUR_END);
    }

    public SkiPass issueWeekendsSkiPassForMorning(DateTime startingDay) {
        checkWeekend(startingDay);

        return issueSkiPassForPartOfDay(startingDay,
                EVENING_HOUR_START, EVENING_HOUR_END);
    }

    private void checkWorkday(DateTime startingDay) {
        Property dayOfWeek = startingDay.dayOfWeek();

        if (dayOfWeek.get() > DateTimeConstants.FRIDAY) {
            String message = "Only Monday to Friday allowed but was: "
                    + dayOfWeek.getAsString();
            throw new IllegalWorkdayException(message);
        }
    }

    public SkiPass issueWeekendsSkiPassForEvening(DateTime startingDay) {
        checkWeekend(startingDay);

        return issueSkiPassForPartOfDay(startingDay,
                EVENING_HOUR_START, EVENING_HOUR_END);
    }

    private void checkWeekend(DateTime startingDay) {
        Property dayOfWeek = startingDay.dayOfWeek();

        if (dayOfWeek.get() < DateTimeConstants.SATURDAY) {
            String message = "Only Saturday to Sunday allowed but was: "
                    + dayOfWeek.getAsString();
            throw new IllegalWeekendException(message);
        }
    }

    private SkiPass issueSkiPassForPartOfDay(DateTime startingDay,
                                             int hourStart, int hourEnd) {
        DateTime since = startingDay.withHourOfDay(hourStart);
        DateTime until = startingDay.withHourOfDay(hourEnd);

        SkiPassDetails details = new DurationSkiPassDetails(since, until);
        return new SkiPass(details);
    }

    public SkiPass issueOneWorkdaySkiPass(DateTime startingDay) {
        checkWorkday(startingDay);

        return issueSkiPassForPartOfDay(startingDay,
                MORNING_HOUR_START, EVENING_HOUR_END);
    }

    public SkiPass issueTwoWorkdaysSkiPass(DateTime startingDay) {
        checkWorkday(startingDay);

        DateTime since = startingDay.withHourOfDay(MORNING_HOUR_START);
        DateTime until = startingDay.withHourOfDay(EVENING_HOUR_END)
                .plusDays(1);

        SkiPassDetails details = new DurationSkiPassDetails(since, until);

        return new SkiPass(details);
    }


    public class SkiPass {
        private int id;
        private boolean blocked;
        private SkiPassDetails details;

        private SkiPass(int id) {
            this.id = id;
        }

        public SkiPass(SkiPassDetails details) {
            this.details = details;
        }

        public int getId() {
            return id;
        }

        public boolean isBlocked() {
            return blocked;
        }

        public boolean isAllowed(Date date) {
            return !blocked && !isExpired(date);
        }

        private boolean isExpired(Date date) {
            return false;
        }

        public void withdrawLifting(Date date) {
            if (!isAllowed(date)) {
                throw new IllegalSkiPassAccessException();
            }


        }

        private void block() {
            blocked = true;
        }

        public SkiPassDetails getDetails() {
            return null;
        }
    }

    public SkiPass issue() {
        return this.new SkiPass(skiPassIdCounter++);
    }

    public void block(SkiPass skiPass) {
        skiPass.block();
    }

}
